﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using AbpCodeBuilder.CodeHelper;
using DevExpress.XtraEditors.Controls;

namespace AbpCodeBulider.DxUI
{
    internal partial class FrmCode : FrmBase
    {
        #region 变量

        private Thread _mainThread;

        /// <summary>
        /// 导出保存的目录
        /// </summary>
        private string _savePath;

        /// <summary>
        /// 模板文件生成的目录
        /// </summary>
        private string _fileTemplatePath;

        #endregion

        public FrmCode()
        {
            InitializeComponent();
            gvList.CustomDrawRowIndicator += gvList_CustomDrawRowIndicator;
        }


        private void FrmCode_Load(object sender, EventArgs e)
        {
            _FrmMain.refreshAction += BindGridView;

            List<string> list = new List<string>()
            {
                "Entity",
                "AuditedEntity",
                "CreationAuditedEntity",
                "FullAuditedEntity"
            };

            foreach (var item in list)
            {
                cbeBaseClass.Properties.Items.Add(item);
            }

            cbeBaseClass.SelectedIndex = 0;

            chkPk.Properties.Items.Add("int");
            chkPk.Properties.Items.Add("Guid");
            chkPk.Properties.Items.Add("long");
            chkPk.SelectedIndex = 0;
            
            BindGridView();
        }

        #region 方法
        
        #region 获取选中字段的集合

        /// <summary>
        /// 获取选中字段的集合
        /// </summary>
        /// <param name="columnInfos"></param>
        /// <returns></returns>
        private bool GetColumnInfos(out List<ColumnInfo> columnInfos)
        {
            int countNum = 0;
            int pkCount = 0;
            int fkCount = 0;

            List<string> skipFields = new List<string>()
            {
                "CreationTime",
                "CreatorUserId",
                "DeleterUserId",
                "DeletionTime",
                "IsDeleted",
                "LastModificationTime",
                "LastModifierUserId",
                "TenantId"
            };

            #region 获取字段集合

            columnInfos = new List<ColumnInfo>();
            for (int i = 0; i < gvList.RowCount; i++)
            {
                DataRow dr = gvList.GetDataRow(i);
                if (dr[0].ToString() == "True")
                {
                    countNum++;
                    
                    var colName = dr["列名"].ToString();

                    if (skipFields.Contains(colName))
                    {
                        continue;
                    }

                    ColumnInfo columnInfo = new ColumnInfo();
                    
                    if (dr["主键"].ToString() == "v" || dr["主键"].ToString() == "√")
                    {
                        columnInfo.IsPrimaryKey = true;
                        pkCount = 1;
                        countNum--;
                    }
                    if (dr["外键"].ToString() == "v" || dr["外键"].ToString() == "√")
                    {
                        columnInfo.IsForeignKey = true;
                    }
                    if (dr["标识"].ToString() == "v" || dr["标识"].ToString() == "√")
                    {
                        columnInfo.IsIdentity = true;
                        fkCount = 1;
                    }

                    if (dr["允许空"].ToString() == "v" || dr["允许空"].ToString() == "√")
                    {
                        columnInfo.Nullable = true;
                    }

                    if (!string.IsNullOrWhiteSpace(dr["字段说明"].ToString()))
                    {
                        columnInfo.Description = dr["字段说明"].ToString();
                    }
                    else
                    {
                        columnInfo.Description = colName;
                    }

                    columnInfo.ColumnName = colName;
                    columnInfo.Length = dr["长度"].ToString();
                    
                    columnInfo.ColumnType = CodeCommon.DbTypeToCS(dr["数据类型"].ToString());

                    string defVal = dr["默认值"].ToString();

                    columnInfo.DefaultVal = defVal == "((0))" ? "0" : dr["默认值"].ToString();

                    columnInfo.Scale = dr["小数位数"].ToString();

                    columnInfos.Add(columnInfo);
                }
            }
            #endregion

            #region 判断该表是否含有主键、选择要生成的数据列

            if (countNum == 0 && fkCount != 1 && pkCount != 1)
            {
                MessageBox.Show("请选择主键外的数据!");
                return true;
            }
            if (countNum == 0)
            {
                MessageBox.Show("请选择数据！");
                return true;
            }

            if (pkCount != 1)
            {
                //MessageBox.Show("没有选择主键或者数据没有创建主键！如没创建主键，请创建主键或者唯一标示符！");
                MessageBox.Show("所选择的表必须含有主键！");
                return true;
            }

            #endregion

            return false;
        }

        #endregion

        #region 刷新代码预览界面

        private void UIShowCode(string frmName, string modelCode)
        {
            FrmRec.UIShowCode(this, frmName, modelCode);
        }

        #endregion

        #region 获取数据表的字段

        /// <summary>
        /// 获取数据表的字段 </summary>
        public void BindGridView()
        {
            txtTableName.Text = DataTableName;
            txtEntityName.Text = CodeCommon.RemoveLastS(DataTableName).Replace("Abp", "");
            txtEntityPluralName.Text = DataTableName;

            gcList.DataSource = null;
            gvList.Columns.Clear();
            DataColumn dcolStatus = new DataColumn();
            dcolStatus.DataType = typeof(Boolean);
            dcolStatus.ColumnName = "状态";
            dcolStatus.DefaultValue = true;

            DataTable dt = new DataTable();
            dt.Columns.Add(dcolStatus);

            if (string.IsNullOrEmpty(DataTableId))
            {
                return;
            }

            #region 获取数据库字段信息的sql语句
            string strSql =
                    string.Format(@"SELECT 
	                --(case when a.colorder=1 then d.name else '' end) 表名, 
	                --a.colorder 字段序号, 
                    a.name 列名,
	                b.name 数据类型,
	                (case when COLUMNPROPERTY( a.id,a.name,'IsIdentity')=1 then '√'else '' end) 标识,
	                (case when 
			                (SELECT count(*) FROM sysobjects WHERE 
				                (name in 
					                (SELECT name FROM sysindexes WHERE (id = a.id) AND 
						                (indid in 
							                (SELECT indid FROM sysindexkeys 
								                WHERE (id = a.id) AND 
									                (colid in 
										                (SELECT colid FROM syscolumns WHERE (id = a.id) AND (name = a.name))
									                )
							                )
						                )
					                )
				                ) AND (xtype = 'PK') 
			                ) > 0 then '√' else '' end
	                ) 主键,
	                (
		                case when
			                (
				                select count(fkeyid) from sys.sysforeignkeys f 
				                where f.fkeyid=a.id and f.fkey=a.colid and 
				                f.constid in
				                (
				                  select distinct(id) 
				                   from sysobjects
				                   where OBJECT_NAME(parent_obj)='{0}' and xtype='F'
				                )
				
			                ) >0 then '√' else '' end
	                ) AS 外键,
                    (
						case 
							when b.name='text' then NULL
							when b.name='datetime' then NULL
							else COLUMNPROPERTY(a.id,a.name,'PRECISION')
							end
	                ) AS 长度,
	                --a.length 字段类型可容纳的最大长度,
	                --COLUMNPROPERTY(a.id,a.name,'PRECISION') as 实际长度,
	                (case when a.isnullable=1 then '√'else '' end) 允许空,
	                isnull(COLUMNPROPERTY(a.id,a.name,'Scale'),0) as 小数位数,
	                isnull(e.text,'') 默认值,
	                isnull(g.[value],'') AS 字段说明   
                FROM  syscolumns a 
                left join systypes b on a.xtype=b.xusertype
                inner join sysobjects d on a.id=d.id  and d.name<>'dtproperties' and d.xtype='{0}'
                left join syscomments e on a.cdefault=e.id
                left join sys.extended_properties g on a.id=g.major_id AND a.colid = g.minor_id  
                where d.name in ('{1}')---查询具体的表，注释掉后就是查询整个数据库了
                order by a.id,a.colorder", Xtype, DataTableName);


            //DataTable dtDetails =
            //    SQLHelper.Query(
            //        "select sys.all_columns.name as 列名 ,sys.types.name as 数据类型,sys.all_columns.max_length as 长度,sys.all_columns.is_nullable as 允许空,sys.all_columns.is_identity as 主键 from sys.all_columns INNER JOIN sys.types ON sys.all_columns.user_type_id = sys.types.user_type_id  where object_id=" +
            //        datatableId, databaseName);


            DataTable dtDetails = SqlHelper.Query(strSql, DBName);
            #endregion

            dt.Merge(dtDetails);
            gcList.DataSource = dt;

            for (int i = 1; i < gvList.Columns.Count; i++)
            {
                gvList.Columns[i].OptionsColumn.AllowEdit = false;
            }
            gvList.BestFitColumns();
        }
        #endregion

        #region 生成代码

        /// <summary>
        /// 生成代码
        /// </summary>
        public void GenerateCode(bool preview)
        {
            Properties.Settings.Default.Save();

            if (GetColumnInfos(out var columnInfos)) return;

            _savePath = txtSaveCodeDir.Text.Trim();
            _fileTemplatePath = this.txtNamespace.Text.Trim();

            GenerateMetaInfo generateMetaInfo = new GenerateMetaInfo
            {
                CompanyName = txtCompanyName.Text.Trim(),
                ProjectName = txtProjectName.Text.Trim(),
                NamespaceRelativeFull = txtEntityPluralName.Text.Trim().Replace("Abp", ""),
                TableName = txtTableName.Text.Trim().Replace("Abp", ""),
                EntityName = txtEntityName.Text.Trim().Replace("Abp", ""),
                EntityNamePlural = txtEntityPluralName.Text.Trim().Replace("Abp", ""),
                BaseClass = cbeBaseClass.EditValue.ToString()
            };

            generateMetaInfo.PermissionName = chkAdminPermission.Checked ? $"Pages_Administration_{generateMetaInfo.EntityNamePlural}" : $"Pages_{generateMetaInfo.EntityNamePlural}";

            if (!string.IsNullOrWhiteSpace(txtPrefix.Text.Trim()))
            {
                var prefix = txtPrefix.Text.Trim();

                generateMetaInfo.NamespaceRelativeFull =
                    txtEntityPluralName.Text.Trim().Replace("Abp", "").Replace(prefix, "");
                generateMetaInfo.TableName = txtTableName.Text.Trim().Replace("Abp", "").Replace(prefix, "");
                generateMetaInfo.EntityName = txtEntityName.Text.Trim().Replace("Abp", "").Replace(prefix, "");
                generateMetaInfo.EntityNamePlural =
                    txtEntityPluralName.Text.Trim().Replace("Abp", "").Replace(prefix, "");
            }

            generateMetaInfo.PrimaryKeyType =
                columnInfos.Where(x => x.IsPrimaryKey).Select(x => x.ColumnType).SingleOrDefault();
            generateMetaInfo.PrimaryKeyName =
                columnInfos.Where(x => x.IsPrimaryKey).Select(x => x.ColumnName).SingleOrDefault();
            
            //生成服务端代码
            if (chkServer.Checked)
            {
                GenerateServer(preview, generateMetaInfo, columnInfos);
            }

            //生成前端代码
            if (chkClient.Checked)
            {
                GenerateClient(preview, generateMetaInfo, columnInfos);
            }

            //生成多语言
            if (chkLocalization.Checked)
            {
                UIShowCode("Localization", ServerGenerator.GenLocalization(generateMetaInfo, columnInfos));
            }

        }

        #region 生成服务端代码

        /// <summary>
        /// 生成服务端代码
        /// </summary>
        private void GenerateServer(bool preview, GenerateMetaInfo generateMetaInfo, List<ColumnInfo> columnInfos)
        {
            if (chkAuthorizationProvider.Checked)
            {
                if (chkAdminPermission.Checked)
                {
                    var adminPermissions = ServerGenerator.GenAdminPermissions(generateMetaInfo.EntityNamePlural);
                    UIShowCode("AdminPermissions", adminPermissions);

                    var adminAuthorizationProvider =
                        ServerGenerator.GenAdminAuthorizationProvider(generateMetaInfo.EntityName,
                            generateMetaInfo.EntityNamePlural);
                    UIShowCode("AdminAuthorizationProvider", adminAuthorizationProvider);
                }
                else
                {
                    var appPermissions = ServerGenerator.GenAppPermissions(generateMetaInfo.EntityNamePlural);
                    UIShowCode("AppPermissions", appPermissions);

                    var appAuthorizationProvider =
                        ServerGenerator.GenAppAuthorizationProvider(generateMetaInfo.EntityName,
                            generateMetaInfo.EntityNamePlural);
                    UIShowCode("AppAuthorizationProvider", appAuthorizationProvider);
                }
            }

            var newPath = Path.Combine(_savePath, "aspnet-core");
            string applicationPath = Path.Combine(newPath, _fileTemplatePath, "Application",
                generateMetaInfo.EntityNamePlural);

            string applicationExportingPath = Path.Combine(newPath, _fileTemplatePath, "Application",
                generateMetaInfo.EntityNamePlural);

            string applicationDtoPath = Path.Combine(newPath, _fileTemplatePath, "Application",
                generateMetaInfo.EntityNamePlural, "Dto");

            string dapperMapperPath = Path.Combine(newPath, _fileTemplatePath, "Application", "DapperMapper");

            var appServiceInterfaceClass =
                ServerGenerator.GenAppServiceInterfaceClass(generateMetaInfo, chkExcelExport.Checked);
            var appServiceClass = ServerGenerator.GenAppServiceClass(generateMetaInfo, chkExcelExport.Checked);
            var exportingIntercafeClass = ServerGenerator.GenExportingIntercafeClass(generateMetaInfo);
            var exportingClass = ServerGenerator.GenExportingClass(generateMetaInfo, columnInfos);
            var getAllForExcelInputClass = ServerGenerator.GenGetAllForExcelInputClass(generateMetaInfo);
            var getAllInputClass = ServerGenerator.GenGetAllInputClass(generateMetaInfo);
            var getForEditOutputClass = ServerGenerator.GenGetForEditOutputClass(generateMetaInfo);
            var entityDtoClass = ServerGenerator.GenEntityDtoClass(generateMetaInfo, columnInfos);
            var createOrEditDtoClass = ServerGenerator.GenCreateOrEditDtoClass(generateMetaInfo, columnInfos);
            var batchDeleteInputClass = ServerGenerator.GenBatchDeleteInputClass(generateMetaInfo);
            var dapperMapperClass = ServerGenerator.GenDapperMapperClass(generateMetaInfo);

            if (preview)
            {
                #region 生成预览

                if (chkAppServiceInterface.Checked) UIShowCode("AppServiceIntercafeClass", appServiceInterfaceClass);
                if (chkAppService.Checked) UIShowCode("AppServiceClass", appServiceClass);
                if (chkGetAllInput.Checked) UIShowCode("AllInputClass", getAllInputClass);
                if (chkGetForEditOutput.Checked) UIShowCode("GetForEditOutputClass", getForEditOutputClass);
                if (chkEntityDto.Checked) UIShowCode("EntityDtoClass", entityDtoClass);
                if (chkCreateOrEditInput.Checked) UIShowCode("CreateOrEditDtoClass", createOrEditDtoClass);
                if (chkBatchDeleteInput.Checked) UIShowCode("BatchDeleteInputClass", batchDeleteInputClass);

                //如果勾选了excel导出
                if (chkExcelExport.Checked)
                {
                    if (chkExportingIntercafe.Checked) UIShowCode("ExportingIntercafeClass", exportingIntercafeClass);
                    if (chkExportingService.Checked) UIShowCode("ExportingServiceClass", exportingClass);
                    if (chkGetAllForExcelInput.Checked)
                        UIShowCode("GetAllForExcelInputClass", getAllForExcelInputClass);
                }

                if (chkDapperMapper.Checked) UIShowCode("DapperMapperClass", dapperMapperClass);

                #endregion
            }
            else
            {
                #region 生成导出

                if (chkAppServiceInterface.Checked)
                    CodeCommon.Write(applicationPath, $"I{generateMetaInfo.EntityName}AppService.cs", appServiceInterfaceClass);

                if (chkAppService.Checked)
                    CodeCommon.Write(applicationPath, $"{generateMetaInfo.EntityName}AppService.cs", appServiceClass);

                if (chkGetAllInput.Checked)
                    CodeCommon.Write(applicationDtoPath, $"GetAll{generateMetaInfo.EntityNamePlural}Input.cs",
                        getAllInputClass);

                if (chkGetForEditOutput.Checked)
                    CodeCommon.Write(applicationDtoPath, $"Get{generateMetaInfo.EntityName}ForEditOutput.cs",
                        getForEditOutputClass);

                if (chkEntityDto.Checked)
                    CodeCommon.Write(applicationDtoPath, $"{generateMetaInfo.EntityName}Dto.cs", entityDtoClass);

                if (chkCreateOrEditInput.Checked)
                    CodeCommon.Write(applicationDtoPath, $"CreateOrEdit{generateMetaInfo.EntityName}Dto.cs",
                        createOrEditDtoClass);

                if (chkBatchDeleteInput.Checked)
                    CodeCommon.Write(applicationDtoPath, $"BatchDelete{generateMetaInfo.EntityNamePlural}Input.cs",
                        batchDeleteInputClass);

                //如果勾选了excel导出
                if (chkExcelExport.Checked)
                {
                    if (chkExportingIntercafe.Checked)
                        CodeCommon.Write(applicationExportingPath, $"I{generateMetaInfo.EntityName}ExcelExporter.cs",
                            exportingIntercafeClass);

                    if (chkExportingService.Checked)
                        CodeCommon.Write(applicationExportingPath, $"{generateMetaInfo.EntityName}ExcelExporter.cs",
                            exportingClass);

                    if (chkGetAllForExcelInput.Checked)
                        CodeCommon.Write(applicationDtoPath,
                            $"GetAll{generateMetaInfo.EntityNamePlural}ForExcelInput.cs", getAllForExcelInputClass);
                }


                if (chkDapperMapper.Checked)
                    CodeCommon.Write(dapperMapperPath, $"{generateMetaInfo.EntityName}Mapper.cs",
                        dapperMapperClass);

                #endregion
            }
        }

        #endregion

        #region 生成前端代码

        /// <summary>
        /// 生成前端代码
        /// </summary>
        private void GenerateClient(bool preview, GenerateMetaInfo generateMetaInfo, List<ColumnInfo> columnInfos)
        {
            var vueModule = ClientGenerator.GenModule(generateMetaInfo, columnInfos, chkExcelExport.Checked);
            var vueView = ClientGenerator.GenView(generateMetaInfo, columnInfos, chkExcelExport.Checked);

            if (preview)
            {
                UIShowCode("VueModule", vueModule);
                UIShowCode("VueView", vueView);
            }
            else
            {
                var newPath = Path.Combine(_savePath, "vue");
                string modulePath = Path.Combine(newPath, "src","store", "modules");
                string viewPath = Path.Combine(newPath, "src", "views",
                    $"{CodeCommon.GetFirstToLowerStr(generateMetaInfo.EntityNamePlural)}");

                CodeCommon.Write(modulePath, $"{CodeCommon.GetFirstToLowerStr(generateMetaInfo.EntityName)}.js", vueModule);
                CodeCommon.Write(viewPath, $"{CodeCommon.GetFirstToLowerStr(generateMetaInfo.EntityNamePlural)}.vue", vueView);
            }
        } 

        #endregion

        #endregion

        #endregion

        #region 事件

        #region 全选
        private void chkSelAll_EditValueChanged(object sender, EventArgs e)
        {
            //if (Convert.ToBoolean(this.chkSelAll.EditValue))
            //{
            //    for (int i = 0; i < gvList.RowCount; i++)
            //    {
            //        gvList.GetDataRow(i)[0] = true;
            //    }
            //}
        }
        #endregion

        #region 反选
        private void chkSelNo_EditValueChanged(object sender, EventArgs e)
        {
            for (int i = 0; i < gvList.RowCount; i++)
            {
                bool result = Convert.ToBoolean(gvList.GetDataRow(i)[0]);

                gvList.GetDataRow(i)[0] = !result;
            }
        }
        #endregion

        #region 清空
        private void chkSelClear_EditValueChanged(object sender, EventArgs e)
        {
            for (int i = 0; i < gvList.RowCount; i++)
            {
                gvList.GetDataRow(i)[0] = false;
            }
        }
        #endregion

        #region 预览生成

        private void btnCodeGenPreview_Click(object sender, EventArgs e)
        {
            if (string.IsNullOrWhiteSpace(txtCompanyName.Text))
            {
                MessageBox.Show("请填写公司名称");
                txtCompanyName.Focus();
                return;
            }
            if (string.IsNullOrWhiteSpace(txtProjectName.Text))
            {
                MessageBox.Show("请填写项目名称");
                txtProjectName.Focus();
                return;
            }
            if (string.IsNullOrWhiteSpace(txtEntityName.Text))
            {
                MessageBox.Show("请填写实体名");
                txtEntityName.Focus();
                return;
            }
            if (string.IsNullOrWhiteSpace(txtEntityPluralName.Text))
            {
                MessageBox.Show("请填写实体名(Plural)");
                txtEntityPluralName.Focus();
                return;
            }


            WaitForm.ShowWaitForm();
            _mainThread = new Thread(() =>
            {
                GenerateCode(true);
                WaitForm.CloseWaitForm();
            });
            _mainThread.Start();
        }

        #endregion
        
        #region 选择---弹出保存目录的对话框

        private void btnOpenSaveDir_Click(object sender, EventArgs e)
        {
            FolderBrowserDialog fbd = new FolderBrowserDialog();
            if (fbd.ShowDialog() == DialogResult.OK)
            {
                this.txtSaveCodeDir.Text = fbd.SelectedPath + @"\AbpCodeBuilder";
            }
        }

        #endregion

        #region 导出

        private void btnOutput_Click(object sender, EventArgs e)
        {
            WaitForm.ShowWaitForm();
            _mainThread = new Thread(() =>
            {
                GenerateCode(false);
                WaitForm.CloseWaitForm();
            });
            _mainThread.Start();
        }


        #endregion

        private void txtProjectName_EditValueChanging(object sender, ChangingEventArgs e)
        {
            this.txtNamespace.Text = $"{this.txtCompanyName.Text}.{this.txtProjectName.Text}";
        }

        private void txtCompanyName_EditValueChanging(object sender, ChangingEventArgs e)
        {
            this.txtNamespace.Text = $"{this.txtCompanyName.Text}.{this.txtProjectName.Text}";
        }

        private void chkAllDto_EditValueChanged(object sender, EventArgs e)
        {
            chkDapperMapper.Checked = this.chkAllDto.Checked;
            chkAppServiceInterface.Checked = this.chkAllDto.Checked;
            chkAppService.Checked = this.chkAllDto.Checked;
            chkExportingIntercafe.Checked = this.chkAllDto.Checked;
            chkExportingService.Checked = this.chkAllDto.Checked;
            chkGetAllForExcelInput.Checked = this.chkAllDto.Checked;
            chkGetAllInput.Checked = this.chkAllDto.Checked;
            chkGetForEditOutput.Checked = this.chkAllDto.Checked;
            chkEntityDto.Checked = this.chkAllDto.Checked;
            chkCreateOrEditInput.Checked = this.chkAllDto.Checked;
            chkBatchDeleteInput.Checked = this.chkAllDto.Checked;
            chkAuthorizationProvider.Checked = this.chkAllDto.Checked;
        }

        #endregion

    }
}
